package org.smile.db.handler;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.smile.beans.converter.BasicConverter;
import org.smile.beans.converter.Converter;
import org.smile.db.dao.ConfigContext;
import org.smile.exception.ConvertException;
import org.smile.exception.JSONParseException;
import org.smile.json.JSONArray;
import org.smile.json.JSONObject;
import org.smile.util.ClassTypeUtils;

import common.Logger;
/**
 * 把结果集转成Bean的RowHandler
 * 
 * 
 * @author strive
 *
 */
public class FieldBeanRowHandler implements RowHandler {
	/**
	 * bean的类型
	 */
	private Class type;
	
	protected static final int PROPERTY_NOT_FOUND = -1;
	
	protected  static final Logger logger=Logger.getLogger(FieldBeanRowHandler.class);
	/**
	 * 当从结果集中取到值为空时，设置的默认值
	 */
	private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();
	
	private PropertyDescriptor[] props ;
	private int[] columnToProperty ;
	private ResultSetMetaData rsmd ;
	
	private Map<PropertyDescriptor,Class[]> genericClass=new HashMap<PropertyDescriptor,Class[]>();
	
	private Converter converter=new BasicConverter();
	
	/**
	 * 构造方法 
	 * @param type 要把结果集处理成Bean的类型
	 */
	public FieldBeanRowHandler(Class type){
		this.type=type;
		try {
			props=propertyDescriptors(type);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 实现接口方法 
	 */
	public Object handle(ResultSet rs) throws SQLException {
        
        if(columnToProperty==null){
        	rsmd= rs.getMetaData();
        	columnToProperty=this.mapColumnsToProperties(rsmd, props);
        }
        return this.createBean(rs, type, props, columnToProperty);
	}
	
	private PropertyDescriptor[] propertyDescriptors(Class<?> c)throws SQLException {
	    BeanInfo beanInfo = null;
	    try {
	        beanInfo = Introspector.getBeanInfo(c);
	
	    } catch (IntrospectionException e) {
	        throw new SQLException(
	            "Bean introspection failed: " + e.getMessage());
	    }
	
	    return beanInfo.getPropertyDescriptors();
	}
	
	protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
            PropertyDescriptor[] props) throws SQLException {

        int cols = rsmd.getColumnCount();
        int[] columnToProperty = new int[cols + 1];
        Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
        Map<String,Field> fields=ConfigContext.getInstance().getDataField(type);
        for (int col = 1; col <= cols; col++) {
            String columnName = rsmd.getColumnLabel(col);
            if (null == columnName || 0 == columnName.length()) {
              columnName = rsmd.getColumnName(col);
            }
            for (int i = 0; i < props.length; i++) {
            	Field field=fields.get(columnName.toLowerCase());
	            if(field!=null){
	                if (field.getName().equalsIgnoreCase(props[i].getName())) {
	                    columnToProperty[col] = i;
	                    break;
	                }
            	}else{
            		//System.out.println(columnName);
            	}
            }
        }
        return columnToProperty;
    }
	 private <T> T createBean(ResultSet rs, Class<T> type,PropertyDescriptor[] props, int[] columnToProperty) throws SQLException {

	        T bean = this.newInstance(type);

	        for (int i = 1; i < columnToProperty.length; i++) {

	            if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
	                continue;
	            }

	            PropertyDescriptor prop = props[columnToProperty[i]];
	            Class<?> propType = prop.getPropertyType();

	            Object value = this.processColumn(rs, i, propType);

	            if (propType != null && value == null && propType.isPrimitive()) {
	                value = primitiveDefaults.get(propType);
	            }

	            this.callSetter(bean, prop, value);
	        }

	        return bean;
	 }
	 /**
	  * 实现化一个类
	  * @param <T>
	  * @param c
	  * @return
	  * @throws SQLException
	  */
	 protected <T> T newInstance(Class<T> c) throws SQLException {
	        try {
	            return c.newInstance();
	        } catch (InstantiationException e) {
	            throw new SQLException(
	                "Cannot create " + c.getName() + ": " + e);

	        } catch (IllegalAccessException e) {
	            throw new SQLException(
	                "Cannot create " + c.getName() + ": " + e);
	        }
	    }
	 
	 
	 protected Object processColumn(ResultSet rs, int index, Class<?> propType) throws SQLException {

	     if ( !propType.isPrimitive() && rs.getObject(index) == null ) {
	         return null;
	     }
	
	     if (propType.equals(String.class)) {
	         return rs.getString(index);
	
	     } else if (
	         propType.equals(Integer.TYPE) || propType.equals(Integer.class)) {
	         return Integer.valueOf(rs.getInt(index));
	
	     } else if (
	         propType.equals(Boolean.TYPE) || propType.equals(Boolean.class)) {
	         return Boolean.valueOf(rs.getBoolean(index));
	
	     } else if (propType.equals(Long.TYPE) || propType.equals(Long.class)) {
	         return Long.valueOf(rs.getLong(index));
	
	     } else if (
	         propType.equals(Double.TYPE) || propType.equals(Double.class)) {
	         return Double.valueOf(rs.getDouble(index));
	
	     } else if (
	         propType.equals(Float.TYPE) || propType.equals(Float.class)) {
	         return Float.valueOf(rs.getFloat(index));
	
	     } else if (
	         propType.equals(Short.TYPE) || propType.equals(Short.class)) {
	         return Short.valueOf(rs.getShort(index));
	
	     } else if (propType.equals(Byte.TYPE) || propType.equals(Byte.class)) {
	         return Byte.valueOf(rs.getByte(index));
	
	     } else if (propType.equals(Timestamp.class)) {
	         return rs.getTimestamp(index);
	
	     } else {
	         return rs.getObject(index);
	     }

	 }
	 private void callSetter(Object target, PropertyDescriptor prop, Object value) throws SQLException {

		 Method setter = prop.getWriteMethod();
		
		 if (setter == null) {
		     return;
		 }

		 Class<?>[] params = setter.getParameterTypes();
		 try {
		     // convert types for some popular ones
		     if (value != null) {
		         if (value instanceof java.util.Date) {
		             if (params[0].getName().equals("java.sql.Date")) {
		                 value = new java.sql.Date(((java.util.Date) value).getTime());
		             } else
		             if (params[0].getName().equals("java.sql.Time")) {
		                 value = new java.sql.Time(((java.util.Date) value).getTime());
		             } else
		             if (params[0].getName().equals("java.sql.Timestamp")) {
		                 value = new java.sql.Timestamp(((java.util.Date) value).getTime());
		             }
		            
		         }else if(Collection.class.isAssignableFrom( params[0])){
		        	 try {
						Collection result=new JSONArray((String)value);
						value=converter.convert( params[0], ClassTypeUtils.getGeneric(prop), result);
					} catch (JSONParseException e) {
						logger.error("解析JSON格式出错",e);
						throw new SQLException("解析JSON格式出错"+e);
					} catch (ConvertException e) {
						logger.error("类型转换格式出错",e);
						throw new SQLException("类型转换格式出错"+e);
					}
		         }
		         else if(Map.class.isAssignableFrom( params[0])){
		        	 try {
						Map result=new JSONObject((String)value);
						value=converter.convert(params[0], ClassTypeUtils.getGeneric(prop), result);
					} catch (JSONParseException e) {
						logger.error("解析JSON格式出错",e);
						throw new SQLException("解析JSON格式出错"+e);
					} catch (ConvertException e) {
						logger.error("类型转换格式出错",e);
						throw new SQLException("类型转换格式出错"+e);
					}
		         }
		     }
		
		     // Don't call setter if the value object isn't the right type
		     if (this.isCompatibleType(value, params[0])) {
		         setter.invoke(target, new Object[]{value});
		     } else {
		       throw new SQLException(
		           "Cannot set " + prop.getName() + ": incompatible types.");
		     }
		
		 } catch (IllegalArgumentException e) {
		     throw new SQLException(
		         "Cannot set " + prop.getName() + ": " + e.getMessage());
		
		 } catch (IllegalAccessException e) {
		     throw new SQLException(
		         "Cannot set " + prop.getName() + ": " + e.getMessage());
		
		 } catch (InvocationTargetException e) {
		     throw new SQLException(
		         "Cannot set " + prop.getName() + ": " + e.getMessage());
		 }
	 }
	 private boolean isCompatibleType(Object value, Class<?> type) {
	        if (value == null || type.isInstance(value)) {
	            return true;

	        } else if (type.equals(Integer.TYPE) && Integer.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Long.TYPE) && Long.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Double.TYPE) && Double.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Float.TYPE) && Float.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Short.TYPE) && Short.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Byte.TYPE) && Byte.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Character.TYPE) && Character.class.isInstance(value)) {
	            return true;

	        } else if (type.equals(Boolean.TYPE) && Boolean.class.isInstance(value)) {
	            return true;

	        }
	        return false;
	}
	 
	 private Class[] getGenericClass(PropertyDescriptor prop){
		Class[] generic= this.genericClass.get(prop);
		if(generic==null){
			generic= ClassTypeUtils.getGeneric(prop);
			this.genericClass.put(prop,generic);
		}
		return generic;
	 }
}
